package com.stone.framework.batch.support;

import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.springframework.batch.item.file.FlatFileParseException;
import org.springframework.batch.item.file.transform.AbstractLineTokenizer;
import org.springframework.batch.item.file.transform.IncorrectLineLengthException;
import org.springframework.batch.item.file.transform.Range;

public class BytesFixedLengthTokenizer extends AbstractLineTokenizer {

    private Range[] ranges;

    private int maxRange = 0;

    boolean open = false;

    private String encoding = "GBK";

    public void setColumns(Range... ranges) {
        this.ranges = Arrays.asList(ranges).toArray(new Range[ranges.length]);
        calculateMaxRange(ranges);
    }

    private void calculateMaxRange(Range[] ranges) {
        if (ranges == null || ranges.length == 0) {
            maxRange = 0;
            return;
        }

        open = false;
        maxRange = ranges[0].getMin();

        for (int i = 0; i < ranges.length; i++) {
            int upperBound;
            if (ranges[i].hasMaxValue()) {
                upperBound = ranges[i].getMax();
            } else {
                upperBound = ranges[i].getMin();
                if (upperBound > maxRange) {
                    open = true;
                }
            }

            if (upperBound > maxRange) {
                maxRange = upperBound;
            }
        }
    }

    @Override
    protected List<String> doTokenize(String line) {
        List<String> tokens = new ArrayList<String>(ranges.length);
        byte[] lineBytes = null;
        try {
            lineBytes = line.getBytes(this.encoding);
        } catch (UnsupportedEncodingException e) {
            throw new FlatFileParseException("", line);
        }
        int lineLength = lineBytes.length;
        String token = null;

        if (lineLength < maxRange && isStrict()) {
            throw new IncorrectLineLengthException("Line is shorter than max range " + maxRange, maxRange, lineLength);
        }

        if (!open && lineLength > maxRange && isStrict()) {
            throw new IncorrectLineLengthException("Line is longer than max range " + maxRange, maxRange, lineLength);
        }

        for (int i = 0; i < ranges.length; i++) {
            int startPos = ranges[i].getMin() - 1;
            int endPos = ranges[i].getMax();

            if (lineLength >= endPos) {
                try {
                    token = new String(Arrays.copyOfRange(lineBytes, startPos, endPos), this.encoding);
                } catch (UnsupportedEncodingException e) {
                    // do nothing
                }
            } else if (lineLength >= startPos) {
                try {
                    token = new String(Arrays.copyOfRange(lineBytes, startPos, lineLength), this.encoding);
                } catch (UnsupportedEncodingException e) {
                    // do nothing
                }
            } else {
                token = "";
            }
            tokens.add(token);
        }

        return tokens;
    }

    public String getEncoding() {
        return encoding;
    }

    public void setEncoding(String encoding) {
        this.encoding = encoding;
    }

}